加入使用者決策。Agent 討論完成後,使用者可以選擇方向,查看修改後的方案與評分,再批准或駁回。
流程增加後,Discord 裡的訊息也變多了。需求、Agent 討論、PM 草案、Review 評分和決策按鈕接連出現,第一次使用的人需要知道:現在是誰在處理?要繼續等,還是該按按鈕?
今天整理這些訊息的顯示方式。讓不同 Agent 有固定的角色卡片,把長內容分頁,並補上目前階段與下一步提示。
這個專案仍使用一個 Discord Bot 帳號,五位 Agent 的身分顯示在 Embed 卡片裡。Embed 是 Discord 的格式化訊息,可以放標題、欄位、作者名稱和顏色。
presentation/agent_profiles.py 集中管理角色的中文名稱、圖示與色碼。這些資訊由 Python 固定,模型只負責產生討論內容。
| 角色 | 顯示名稱 | 主要工作 |
|---|---|---|
| PM | 專案經理 | 整合草案、整理取捨與套用選擇 |
| Research | 研究分析 | 分析需求與需要確認的資訊 |
| Creative | 創意企劃 | 提出不同方案與創意方向 |
| Finance | 財務評估 | 檢查成本、資源與限制 |
| Review | 品質審查 | 審查方案並說明評分理由 |
建立卡片時,再把角色資料放進 author 與顏色。以下節錄分頁完成後的設定:
author = f"{profile.emoji} {profile.display_name}"
for index, page in enumerate(pages, 1):
page.title = f"{title}|{index}/{len(pages)}"
page.set_author(name=author)
page.set_footer(text=f"{footer}\n第 {index}/{len(pages)} 頁".strip())
如此一來,即使訊息被分成多頁,每張卡片仍看得出是哪個角色、目前是第幾頁。專案與會議識別資料也會保留,方便回頭對照。
PM 方案有五個章節,Review 又包含分數、理由與風險。如果為了塞進一張卡片直接截掉後半段,使用者可能只看到摘要,卻看不到做決策需要的資訊。
所以今天把正式會議訊息改成多頁 Embed。長欄位先按換行拆分,沒有換行才切成固定長度;章節名稱與順序保留,後續片段標示「續」。
Discord 對 Embed 的欄位與整體長度各有上限。例如單一欄位內容最多 1,024 字元,一張 Embed 最多 25 個欄位;同一則訊息內所有 Embed 的文字總和不能超過 6,000 字元。這些限制包含標題、作者和 footer,不能只計算內文。Discord 官方文件
presentation/message_chunks.py 會先拆欄位,再把它們裝進頁面。下面是決定是否換頁的片段:
if len(page.fields) >= 25 or len(page) + len(name) + len(value) + fixed > 5500:
page = discord.Embed(title=title, color=profile.color)
pages.append(page)
page.add_field(name=name, value=value, inline=False)
程式先用 5,500 字元作為裝頁預算,替作者名稱與頁碼留出空間,最後再檢查完整卡片是否符合限制。每則訊息只傳一個 Embed,避免多張卡片合計後超過上限。
程式碼區塊跨頁時,也會在前一段補上結尾,再於下一段重新開啟區塊,保留原本的語言標記。這些格式處理由 Python 完成,不必再呼叫模型重寫內容。
決策和批准按鈕則只放在最後一張操作卡。使用者可以先讀完方案、評分與變動,再到最後一則訊息操作;持久按鈕也只需要登記那則實際訊息。
本機模型需要等待,整場會議又包含多位 Agent。如果期間沒有訊息,使用者很難分辨 Bot 正在思考,還是流程已經中斷。
今天新增進度卡。模型呼叫前先顯示負責的 Agent、目前階段與輪次;若發生重試,也會更新嘗試次數。進入等待選擇或批准時,卡片會改成等待操作的狀態。
服務層使用 WorkflowEvent 傳出進度,顯示層再把事件轉成 Discord 卡片。會議服務因此不需要直接建立 Embed。
async def emit_progress(record, stage, state="started", **kwargs):
await emit_event(WorkflowEvent(
record.guild_id,
record.project_id,
record.meeting_id,
stage,
state,
decision_version=record.decision_version,
**kwargs,
))
進度會編輯同一則頻道訊息,減少大量通知。相同階段的密集更新會稍微合併,階段變更、失敗或等待操作則立即顯示。長流程的進度更新使用一般頻道訊息,不依賴最初指令的互動回覆。
這裡顯示的是程式正在執行的階段,例如「PM 整合草案」或「候選方案重新評分」,不會猜測完成百分比。
進度卡適合跟著流程看,但使用者可能中途離開,或剛好錯過決策訊息。因此新增 /status,可以隨時查看目前階段、操作者、操作位置與下一步。
| 目前狀態 | 顯示的操作提示 |
|---|---|
| 需求收集中 | 回原入口頻道補齊目前缺少的資料 |
| Agent 處理中 | 等待結果,可繼續查詢狀態 |
| 等待選擇 | 原啟動者閱讀選項,按 A/B/C |
| 等待批准 | 檢查候選方案與風險,再批准或駁回 |
| 處理中斷 | 依已保存的進度提供接續方式 |
| 已批准完成 | 回入口建立下一個專案 |
如果有有效的決策訊息,狀態卡會附上連結,讓使用者直接跳過去。訊息遺失時,也會提示用 /review 重新顯示。
/status 讀取已保存的會議資料,再參考當次執行事件,不會為了查詢狀態重新呼叫模型。需求還在收集階段時,則顯示暫存對話目前需要的欄位。
中斷後的接續方式也分開處理:分段討論新增 /resume;審查中斷用 /review;已保存選擇的流程,使用相同 /decide choice 接續。原本的 /start 會拒絕已進行中的專案,因此不能直接拿它當通用重試指令。
除了整理卡片,也把訊息依用途分到四個頻道:
#project-requests:提出需求與補齊專案資料。#agent-meeting:Agent 討論、方案、評分與決策操作。#project-status:進度與下一步提示。#dev-log:執行用量與開發紀錄。管理員先建立頻道,再把 ID 填入 .env。Bot 會依設定傳送訊息,不會自行建立頻道。
PROJECT_INTAKE_CHANNEL_ID=你的_requests_頻道ID
AGENT_MEETING_CHANNEL_ID=你的_meeting_頻道ID
PROJECT_STATUS_CHANNEL_ID=你的_status_頻道ID
DEV_LOG_CHANNEL_ID=你的_dev_log_頻道ID
Bot 需要檢視頻道、傳送訊息與 Embed Links 權限。傳送前會檢查頻道是否屬於目前伺服器,以及 Bot 是否有權限;討論或狀態頻道無法使用時,會嘗試回到原操作頻道。
開發紀錄則採不同處理:無法傳到指定 dev-log 時,不會把紀錄轉貼到一般業務頻道。裡面只顯示用量與必要識別資料,不放完整 Prompt、需求內容或 Token 憑證。
這組頻道 ID 對應主要伺服器,其他伺服器會使用原頻道。顯示層也關閉自動 mention 通知,避免模型輸出或狀態卡意外通知其他成員。
這些測試使用固定模型回覆和模擬 Discord 物件,能確認程式處理方式,但還需要在桌面與手機上實際看排版。DAY 26 的 Discord 顯示與首次使用者驗收,目前仍待執行。
驗收時會請首次使用者只看歡迎文字與 /help,獨立走過需求收集、查看草案、選擇方向與批准流程。除了能完成操作,也要確認他在中途說得出現在的階段、誰能決定,以及下一步要去哪裡操作。
專案入口的操作已經展示過了,所以我有這也不放圖片了。
agent-meeting的頻道















project-status的頻道
!

dev-log的頻道就不貼上來了,這個的主要用途讓我來觀察LLM生產的東西會不會產太多token而出現error。
近期升級了電腦,也把本機模型換成 qwen3.5:9b。執行時間比之前長,但實際用下來,產出的內容也更實用。之前有些回答看起來有回應問題,仔細看卻有點答非所問,甚至讓人覺得「怎麼有點笨笨的」。換成新模型後,回答比較能抓到需求,提出的建議也更值得拿來討論。
對這個專案來說,我願意多等一些時間,換取比較有用的方案。不過,等待時還是需要知道 Bot 正在做什麼,因此這次加入的 Agent 身分與進度提示,也剛好派上用場。